From 5a2f829a407241d784dd61e417f4d3166ab5e7a3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 19 Feb 2020 20:51:03 -0500 Subject: [PATCH] Split off GtkEventControllerFocus Split the focus tracking into a separate GtkEventControllerFocus, and change the API one more time. We are back to having ::focus-in and ::focus-out signals. Update all users. --- gtk/gtk.h | 1 + gtk/gtkcalendar.c | 18 +- gtk/gtkentrycompletion.c | 12 +- gtk/gtkentryprivate.h | 1 + gtk/gtkeventcontrollerfocus.c | 361 ++++++++++++++++++++++++++++++++++ gtk/gtkeventcontrollerfocus.h | 66 +++++++ gtk/gtkeventcontrollerkey.c | 244 ----------------------- gtk/gtkeventcontrollerkey.h | 14 -- gtk/gtkfilechooserentry.c | 13 +- gtk/gtkmodelbutton.c | 21 +- gtk/gtkpopovermenu.c | 14 +- gtk/gtkspinbutton.c | 15 +- gtk/gtktext.c | 77 ++++---- gtk/gtktextview.c | 100 +++++----- gtk/gtktreeview.c | 29 ++- gtk/gtktreeviewcolumn.c | 9 +- gtk/gtkwindow.c | 54 ++--- gtk/meson.build | 1 + testsuite/gtk/focus.c | 73 ++++--- 19 files changed, 662 insertions(+), 461 deletions(-) create mode 100644 gtk/gtkeventcontrollerfocus.c create mode 100644 gtk/gtkeventcontrollerfocus.h diff --git a/gtk/gtk.h b/gtk/gtk.h index 07236ce5f1..46c7aabacc 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -101,6 +101,7 @@ #include #include #include +#include #include #include #include diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c index 36470a711d..4ef0721072 100644 --- a/gtk/gtkcalendar.c +++ b/gtk/gtkcalendar.c @@ -109,6 +109,7 @@ #include "gtkgesturedrag.h" #include "gtkeventcontrollerscroll.h" #include "gtkeventcontrollerkey.h" +#include "gtkeventcontrollerfocus.h" #include "gtkdragsource.h" #include "gtknative.h" #include "gtkicontheme.h" @@ -280,8 +281,7 @@ static gboolean gtk_calendar_key_controller_key_pressed (GtkEventControllerKey * guint keycode, GdkModifierType state, GtkWidget *widget); -static void gtk_calendar_key_controller_focus (GtkEventController *controller, - GtkCrossingDirection direction, +static void gtk_calendar_focus_controller_focus (GtkEventController *controller, GtkWidget *widget); static void gtk_calendar_state_flags_changed (GtkWidget *widget, GtkStateFlags previous_state); @@ -592,8 +592,13 @@ gtk_calendar_init (GtkCalendar *calendar) g_signal_connect (controller, "key-pressed", G_CALLBACK (gtk_calendar_key_controller_key_pressed), calendar); - g_signal_connect (controller, "focus-change", - G_CALLBACK (gtk_calendar_key_controller_focus), + gtk_widget_add_controller (GTK_WIDGET (calendar), controller); + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "enter", + G_CALLBACK (gtk_calendar_focus_controller_focus), + calendar); + g_signal_connect (controller, "leave", + G_CALLBACK (gtk_calendar_focus_controller_focus), calendar); gtk_widget_add_controller (GTK_WIDGET (calendar), controller); @@ -1389,9 +1394,8 @@ gtk_calendar_key_controller_key_pressed (GtkEventControllerKey *controller, } static void -gtk_calendar_key_controller_focus (GtkEventController *controller, - GtkCrossingDirection direction, - GtkWidget *widget) +gtk_calendar_focus_controller_focus (GtkEventController *controller, + GtkWidget *widget) { GtkCalendar *calendar = GTK_CALENDAR (widget); GtkCalendarPrivate *priv = gtk_calendar_get_instance_private (calendar); diff --git a/gtk/gtkentrycompletion.c b/gtk/gtkentrycompletion.c index 4167b7e104..a3c642edc8 100644 --- a/gtk/gtkentrycompletion.c +++ b/gtk/gtkentrycompletion.c @@ -83,6 +83,7 @@ #include "gtkentry.h" #include "gtkmain.h" #include "gtkmarshalers.h" +#include "gtkeventcontrollerfocus.h" #include "gtkeventcontrollerkey.h" #include "gtkgestureclick.h" @@ -2308,11 +2309,9 @@ accept_completion_callback (GtkEntryCompletion *completion) } static void -text_focus_change (GtkEntryCompletion *completion, - GtkCrossingDirection direction) +text_focus_out (GtkEntryCompletion *completion) { - if (direction == GTK_CROSSING_OUT && - !gtk_widget_get_mapped (completion->priv->popup_window)) + if (!gtk_widget_get_mapped (completion->priv->popup_window)) accept_completion_callback (completion); } @@ -2349,7 +2348,9 @@ connect_completion_signals (GtkEntryCompletion *completion) controller = priv->entry_key_controller = gtk_event_controller_key_new (); g_signal_connect (controller, "key-pressed", G_CALLBACK (gtk_entry_completion_key_pressed), completion); - g_signal_connect_swapped (controller, "focus-change", G_CALLBACK (text_focus_change), completion); + gtk_widget_add_controller (GTK_WIDGET (text), controller); + controller = priv->entry_focus_controller = gtk_event_controller_focus_new (); + g_signal_connect_swapped (controller, "leave", G_CALLBACK (text_focus_out), completion); gtk_widget_add_controller (GTK_WIDGET (text), controller); completion->priv->changed_id = @@ -2397,6 +2398,7 @@ disconnect_completion_signals (GtkEntryCompletion *completion) GtkText *text = gtk_entry_get_text_widget (GTK_ENTRY (completion->priv->entry)); gtk_widget_remove_controller (GTK_WIDGET (text), completion->priv->entry_key_controller); + gtk_widget_remove_controller (GTK_WIDGET (text), completion->priv->entry_focus_controller); if (completion->priv->changed_id > 0 && g_signal_handler_is_connected (text, completion->priv->changed_id)) diff --git a/gtk/gtkentryprivate.h b/gtk/gtkentryprivate.h index 2f7d8dc695..de765d9b49 100644 --- a/gtk/gtkentryprivate.h +++ b/gtk/gtkentryprivate.h @@ -78,6 +78,7 @@ struct _GtkEntryCompletionPrivate gchar *case_normalized_key; GtkEventController *entry_key_controller; + GtkEventController *entry_focus_controller; /* only used by GtkEntry when attached: */ GtkWidget *popup_window; diff --git a/gtk/gtkeventcontrollerfocus.c b/gtk/gtkeventcontrollerfocus.c new file mode 100644 index 0000000000..2c7f963a9e --- /dev/null +++ b/gtk/gtkeventcontrollerfocus.c @@ -0,0 +1,361 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2020, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Matthias Clasen + */ + +/** + * SECTION:gtkeventcontrollerfocus + * @Short_description: Event controller for focus + * @Title: GtkEventControllerFocus + * @See_also: #GtkEventController + * + * #GtkEventControllerFocus is an event controller meant for situations + * where you need to know where the focusboard focus is. + **/ + +#include "config.h" + +#include "gtkintl.h" +#include "gtkmarshalers.h" +#include "gtkprivate.h" +#include "gtkwidgetprivate.h" +#include "gtkeventcontrollerprivate.h" +#include "gtkeventcontrollerfocus.h" +#include "gtkbindings.h" +#include "gtkenums.h" +#include "gtkmain.h" +#include "gtktypebuiltins.h" + +#include + +struct _GtkEventControllerFocus +{ + GtkEventController parent_instance; + + const GtkCrossingData *current_crossing; + + guint is_focus : 1; + guint contains_focus : 1; +}; + +struct _GtkEventControllerFocusClass +{ + GtkEventControllerClass parent_class; +}; + +enum { + ENTER, + LEAVE, + N_SIGNALS +}; + +static guint signals[N_SIGNALS] = { 0 }; + +enum { + PROP_IS_FOCUS = 1, + PROP_CONTAINS_FOCUS, + NUM_PROPERTIES +}; + +static GParamSpec *props[NUM_PROPERTIES] = { NULL, }; + +G_DEFINE_TYPE (GtkEventControllerFocus, gtk_event_controller_focus, + GTK_TYPE_EVENT_CONTROLLER) + +static void +gtk_event_controller_focus_finalize (GObject *object) +{ + //GtkEventControllerFocus *focus = GTK_EVENT_CONTROLLER_FOCUS (object); + + G_OBJECT_CLASS (gtk_event_controller_focus_parent_class)->finalize (object); +} + +static void +update_focus (GtkEventController *controller, + const GtkCrossingData *crossing) +{ + GtkEventControllerFocus *focus = GTK_EVENT_CONTROLLER_FOCUS (controller); + GtkWidget *widget = gtk_event_controller_get_widget (controller); + gboolean is_focus = FALSE; + gboolean contains_focus = FALSE; + gboolean enter = FALSE; + gboolean leave = FALSE; + + if (crossing->direction == GTK_CROSSING_IN) + { + if (crossing->new_target == widget) + is_focus = TRUE; + if (crossing->new_target != NULL) + contains_focus = TRUE; + } + + if (focus->contains_focus != contains_focus) + { + if (contains_focus) + enter = TRUE; + else + leave = TRUE; + } + + if (leave) + g_signal_emit (controller, signals[LEAVE], 0); + + g_object_freeze_notify (G_OBJECT (focus)); + if (focus->is_focus != is_focus) + { + focus->is_focus = is_focus; + g_object_notify (G_OBJECT (focus), "is-focus"); + } + + if (focus->contains_focus != contains_focus) + { + focus->contains_focus = contains_focus; + g_object_notify (G_OBJECT (focus), "contains-focus"); + } + g_object_thaw_notify (G_OBJECT (focus)); + + if (enter) + g_signal_emit (controller, signals[ENTER], 0); +} + +static void +gtk_event_controller_focus_handle_crossing (GtkEventController *controller, + const GtkCrossingData *crossing, + double x, + double y) +{ + GtkEventControllerFocus *focus = GTK_EVENT_CONTROLLER_FOCUS (controller); + + if (crossing->type != GTK_CROSSING_FOCUS) + return; + + focus->current_crossing = crossing; + + update_focus (controller, crossing); + + focus->current_crossing = NULL; +} + +static void +gtk_event_controller_focus_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkEventControllerFocus *controller = GTK_EVENT_CONTROLLER_FOCUS (object); + + switch (prop_id) + { + case PROP_IS_FOCUS: + g_value_set_boolean (value, controller->is_focus); + break; + + case PROP_CONTAINS_FOCUS: + g_value_set_boolean (value, controller->contains_focus); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtk_event_controller_focus_class_init (GtkEventControllerFocusClass *klass) +{ + GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gtk_event_controller_focus_finalize; + object_class->get_property = gtk_event_controller_focus_get_property; + controller_class->handle_crossing = gtk_event_controller_focus_handle_crossing; + + /** + * GtkEventControllerFocus:is-focus: + * + * Whether focus is in the controllers widget itself, + * opposed to in a descendent widget. See also + * #GtkEventControllerFocus:contains-focus. + * + * When handling focus events, this property is updated + * before #GtkEventControllerFocus::focus-in or + * #GtkEventControllerFocus::focus-out are emitted. + */ + props[PROP_IS_FOCUS] = + g_param_spec_boolean ("is-focus", + P_("Is Focus"), + P_("Whether the focus is in the controllers widget"), + FALSE, + G_PARAM_READABLE); + + /** + * GtkEventControllerFocus:contains-focus: + * + * Whether focus is contain in the controllers widget. See + * See #GtkEventControllerFocus:is-focus for whether the focus is in the widget itself + * or inside a descendent. + * + * When handling focus events, this property is updated + * before #GtkEventControllerFocus::focus-in or + * #GtkEventControllerFocus::focus-out are emitted. + */ + props[PROP_CONTAINS_FOCUS] = + g_param_spec_boolean ("contains-focus", + P_("Contains Focus"), + P_("Whether the focus is in a descendant of the controllers widget"), + FALSE, + G_PARAM_READABLE); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, props); + + /** + * GtkEventControllerFocus::enter: + * @controller: the object which received the signal + * + * This signal is emitted whenever the focus enters into the + * widget or one of its descendents. + */ + signals[ENTER] = + g_signal_new (I_("enter"), + GTK_TYPE_EVENT_CONTROLLER_FOCUS, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + /** + * GtkEventControllerFocus::leave: + * @controller: the object which received the signal + * + * This signal is emitted whenever the focus leaves from + * the widget or one of its descendents. + */ + signals[LEAVE] = + g_signal_new (I_("leave"), + GTK_TYPE_EVENT_CONTROLLER_FOCUS, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + NULL, + G_TYPE_NONE, 0); +} + +static void +gtk_event_controller_focus_init (GtkEventControllerFocus *controller) +{ +} + +/** + * gtk_event_controller_focus_new: + * + * Creates a new event controller that will handle focus events. + * + * Returns: a new #GtkEventControllerFocus + **/ +GtkEventController * +gtk_event_controller_focus_new (void) +{ + return g_object_new (GTK_TYPE_EVENT_CONTROLLER_FOCUS, NULL); +} + +/** + * gtk_event_controller_focus_get_focus_origin: + * @controller: a #GtkEventControllerFocus + * + * Returns the widget that was holding focus before. + * + * This function can only be used in handlers for the + * #GtkEventControllerFocus::focus-in and #GtkEventControllerFocus::focus-out signals. + * + * Returns: (transfer none): the previous focus + */ +GtkWidget * +gtk_event_controller_focus_get_focus_origin (GtkEventControllerFocus *controller) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_FOCUS (controller), NULL); + g_return_val_if_fail (controller->current_crossing != NULL, NULL); + + return controller->current_crossing->old_target; +} + +/** + * gtk_event_controller_focus_get_focus_target: + * @controller: a #GtkEventControllerFocus + * + * Returns the widget that will be holding focus afterwards. + * + * This function can only be used in handlers for the + * #GtkEventControllerFocus::focus-in and #GtkEventControllerFocus::focus-out signals. + * + * Returns: (transfer none): the next focus + */ +GtkWidget * +gtk_event_controller_focus_get_focus_target (GtkEventControllerFocus *controller) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_FOCUS (controller), NULL); + g_return_val_if_fail (controller->current_crossing != NULL, NULL); + + return controller->current_crossing->new_target; +} + +GtkWidget * +gtk_event_controller_focus_get_old_focus_child (GtkEventControllerFocus *controller) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_FOCUS (controller), NULL); + g_return_val_if_fail (controller->current_crossing != NULL, NULL); + + return controller->current_crossing->old_descendent; +} + +GtkWidget * +gtk_event_controller_focus_get_new_focus_child (GtkEventControllerFocus *controller) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_FOCUS (controller), NULL); + g_return_val_if_fail (controller->current_crossing != NULL, NULL); + + return controller->current_crossing->new_descendent; +} + +/** + * gtk_event_controller_focus_contains_focus: + * @self: a #GtkEventControllerFocus + * + * Returns the value of the GtkEventControllerFocus:contains-focus property. + * + * Returns: %TRUE if focus is within @self or one of its children + */ +gboolean +gtk_event_controller_focus_contains_focus (GtkEventControllerFocus *self) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_FOCUS (self), FALSE); + + return self->contains_focus; +} + +/** + * gtk_event_controller_focus_is_focus: + * @self: a #GtkEventControllerFocus + * + * Returns the value of the GtkEventControllerFocus:is-focus property. + * + * Returns: %TRUE if focus is within @self but not one of its children + */ +gboolean +gtk_event_controller_focus_is_focus (GtkEventControllerFocus *self) +{ + g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_FOCUS (self), FALSE); + + return self->is_focus; +} diff --git a/gtk/gtkeventcontrollerfocus.h b/gtk/gtkeventcontrollerfocus.h new file mode 100644 index 0000000000..3a4ae1b7fe --- /dev/null +++ b/gtk/gtkeventcontrollerfocus.h @@ -0,0 +1,66 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2020, Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author(s): Matthias Clasen + */ + +#ifndef __GTK_EVENT_CONTROLLER_FOCUS_H__ +#define __GTK_EVENT_CONTROLLER_FOCUS_H__ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_EVENT_CONTROLLER_FOCUS (gtk_event_controller_focus_get_type ()) +#define GTK_EVENT_CONTROLLER_FOCUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_FOCUS, GtkEventControllerFocus)) +#define GTK_EVENT_CONTROLLER_FOCUS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_FOCUS, GtkEventControllerFocusClass)) +#define GTK_IS_EVENT_CONTROLLER_FOCUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_FOCUS)) +#define GTK_IS_EVENT_CONTROLLER_FOCUS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_FOCUS)) +#define GTK_EVENT_CONTROLLER_FOCUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_FOCUS, GtkEventControllerFocusClass)) + +typedef struct _GtkEventControllerFocus GtkEventControllerFocus; +typedef struct _GtkEventControllerFocusClass GtkEventControllerFocusClass; + +GDK_AVAILABLE_IN_ALL +GType gtk_event_controller_focus_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_ALL +GtkEventController *gtk_event_controller_focus_new (void); + +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_event_controller_focus_get_focus_origin (GtkEventControllerFocus *controller); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_event_controller_focus_get_focus_target (GtkEventControllerFocus *controller); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_event_controller_focus_get_old_focus_child (GtkEventControllerFocus *controller); +GDK_AVAILABLE_IN_ALL +GtkWidget * gtk_event_controller_focus_get_new_focus_child (GtkEventControllerFocus *controller); + +GDK_AVAILABLE_IN_ALL +gboolean gtk_event_controller_focus_contains_focus (GtkEventControllerFocus *self); +GDK_AVAILABLE_IN_ALL +gboolean gtk_event_controller_focus_is_focus (GtkEventControllerFocus *self); + + +G_END_DECLS + +#endif /* __GTK_EVENT_CONTROLLER_FOCUS_H__ */ diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c index 14a79e7b0c..79103f407c 100644 --- a/gtk/gtkeventcontrollerkey.c +++ b/gtk/gtkeventcontrollerkey.c @@ -51,7 +51,6 @@ struct _GtkEventControllerKey GdkModifierType state; GdkEvent *current_event; - const GtkCrossingData *current_crossing; guint is_focus : 1; guint contains_focus : 1; @@ -67,20 +66,11 @@ enum { KEY_RELEASED, MODIFIERS, IM_UPDATE, - FOCUS_CHANGE, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0 }; -enum { - PROP_IS_FOCUS = 1, - PROP_CONTAINS_FOCUS, - NUM_PROPERTIES -}; - -static GParamSpec *props[NUM_PROPERTIES] = { NULL, }; - G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key, GTK_TYPE_EVENT_CONTROLLER) @@ -155,87 +145,6 @@ gtk_event_controller_key_handle_event (GtkEventController *controller, return handled; } -static void -update_focus (GtkEventController *controller, - const GtkCrossingData *crossing) -{ - GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller); - GtkWidget *widget = gtk_event_controller_get_widget (controller); - gboolean is_focus = FALSE; - gboolean contains_focus = FALSE; - - if (crossing->direction == GTK_CROSSING_IN) - { - if (crossing->new_target == widget) - is_focus = TRUE; - if (crossing->new_target != NULL) - contains_focus = TRUE; - } - - g_object_freeze_notify (G_OBJECT (key)); - if (key->is_focus != is_focus) - { - key->is_focus = is_focus; - g_object_notify (G_OBJECT (key), "is-focus"); - if (key->im_context) - { - if (is_focus) - gtk_im_context_focus_in (key->im_context); - else - gtk_im_context_focus_out (key->im_context); - } - } - if (key->contains_focus != contains_focus) - { - key->contains_focus = contains_focus; - g_object_notify (G_OBJECT (key), "contains-focus"); - } - g_object_thaw_notify (G_OBJECT (key)); -} - -static void -gtk_event_controller_key_handle_crossing (GtkEventController *controller, - const GtkCrossingData *crossing, - double x, - double y) -{ - GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller); - - if (crossing->type != GTK_CROSSING_FOCUS) - return; - - key->current_crossing = crossing; - - update_focus (controller, crossing); - - g_signal_emit (controller, signals[FOCUS_CHANGE], 0, crossing->direction); - - key->current_crossing = NULL; -} - -static void -gtk_event_controller_key_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkEventControllerKey *controller = GTK_EVENT_CONTROLLER_KEY (object); - - switch (prop_id) - { - case PROP_IS_FOCUS: - g_value_set_boolean (value, controller->is_focus); - break; - - case PROP_CONTAINS_FOCUS: - g_value_set_boolean (value, controller->contains_focus); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - static void gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) { @@ -243,47 +152,7 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gtk_event_controller_key_finalize; - object_class->get_property = gtk_event_controller_key_get_property; controller_class->handle_event = gtk_event_controller_key_handle_event; - controller_class->handle_crossing = gtk_event_controller_key_handle_crossing; - - /** - * GtkEventControllerKey:is-focus: - * - * Whether focus is in the controllers widget itself, - * opposed to in a descendent widget. See also - * #GtkEventControllerKey:contains-focus. - * - * When handling focus events, this property is updated - * before #GtkEventControllerKey::focus-in or - * #GtkEventControllerKey::focus-out are emitted. - */ - props[PROP_IS_FOCUS] = - g_param_spec_boolean ("is-focus", - P_("Is Focus"), - P_("Whether the focus is in the controllers widget"), - FALSE, - G_PARAM_READABLE); - - /** - * GtkEventControllerKey:contains-focus: - * - * Whether focus is contain in the controllers widget. See - * See #GtkEventControllerKey:is-focus for whether the focus is in the widget itself - * or inside a descendent. - * - * When handling focus events, this property is updated - * before #GtkEventControllerKey::focus-in or - * #GtkEventControllerKey::focus-out are emitted. - */ - props[PROP_CONTAINS_FOCUS] = - g_param_spec_boolean ("contains-focus", - P_("Contains Focus"), - P_("Whether the focus is in a descendant of the controllers widget"), - FALSE, - G_PARAM_READABLE); - - g_object_class_install_properties (object_class, NUM_PROPERTIES, props); /** * GtkEventControllerKey::key-pressed: @@ -365,29 +234,6 @@ gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - - /** - * GtkEventControllerKey::focus-change: - * @controller: the object which received the signal - * @direction: the direction of this crossing event - * - * This signal is emitted whenever the focus change from or - * to a widget that is a descendant of the widget to which - * @controller is attached. - * - * Handlers for this signal can use - * gtk_event_controller_key_get_focus_origin() and - * gtk_event_controller_key_get_focus_target() to find - * the old and new focus locations. - */ - signals[FOCUS_CHANGE] = - g_signal_new (I_("focus-change"), - GTK_TYPE_EVENT_CONTROLLER_KEY, - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - NULL, - G_TYPE_NONE, 1, - GTK_TYPE_CROSSING_DIRECTION); } static void @@ -507,93 +353,3 @@ gtk_event_controller_key_get_group (GtkEventControllerKey *controller) return gdk_key_event_get_group (controller->current_event); } - -/** - * gtk_event_controller_key_get_focus_origin: - * @controller: a #GtkEventControllerKey - * - * Returns the widget that was holding focus before. - * - * This function can only be used in handlers for the - * #GtkEventControllerKey::focus-changed signal. - * - * Returns: (transfer none): the previous focus - */ -GtkWidget * -gtk_event_controller_key_get_focus_origin (GtkEventControllerKey *controller) -{ - g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); - g_return_val_if_fail (controller->current_crossing != NULL, NULL); - - return controller->current_crossing->old_target; -} - -/** - * gtk_event_controller_key_get_focus_target: - * @controller: a #GtkEventControllerKey - * - * Returns the widget that will be holding focus afterwards. - * - * This function can only be used in handlers for the - * #GtkEventControllerKey::focus-changed signal. - * - * Returns: (transfer none): the next focus - */ -GtkWidget * -gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller) -{ - g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); - g_return_val_if_fail (controller->current_crossing != NULL, NULL); - - return controller->current_crossing->new_target; -} - -GtkWidget * -gtk_event_controller_key_get_old_focus_child (GtkEventControllerKey *controller) -{ - g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); - g_return_val_if_fail (controller->current_crossing != NULL, NULL); - - return controller->current_crossing->old_descendent; -} - -GtkWidget * -gtk_event_controller_key_get_new_focus_child (GtkEventControllerKey *controller) -{ - g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL); - g_return_val_if_fail (controller->current_crossing != NULL, NULL); - - return controller->current_crossing->new_descendent; -} - -/** - * gtk_event_controller_key_contains_focus: - * @self: a #GtkEventControllerKey - * - * Returns the value of the GtkEventControllerKey:contains-focus property. - * - * Returns: %TRUE if focus is within @self or one of its children - */ -gboolean -gtk_event_controller_key_contains_focus (GtkEventControllerKey *self) -{ - g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (self), FALSE); - - return self->contains_focus; -} - -/** - * gtk_event_controller_key_is_focus: - * @self: a #GtkEventControllerKey - * - * Returns the value of the GtkEventControllerKey:is-focus property. - * - * Returns: %TRUE if focus is within @self but not one of its children - */ -gboolean -gtk_event_controller_key_is_focus (GtkEventControllerKey *self) -{ - g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (self), FALSE); - - return self->is_focus; -} diff --git a/gtk/gtkeventcontrollerkey.h b/gtk/gtkeventcontrollerkey.h index 4aa22777d6..b24df105d0 100644 --- a/gtk/gtkeventcontrollerkey.h +++ b/gtk/gtkeventcontrollerkey.h @@ -58,20 +58,6 @@ gboolean gtk_event_controller_key_forward (GtkEventControllerK GDK_AVAILABLE_IN_ALL guint gtk_event_controller_key_get_group (GtkEventControllerKey *controller); -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_event_controller_key_get_focus_origin (GtkEventControllerKey *controller); -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_event_controller_key_get_focus_target (GtkEventControllerKey *controller); -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_event_controller_key_get_old_focus_child (GtkEventControllerKey *controller); -GDK_AVAILABLE_IN_ALL -GtkWidget * gtk_event_controller_key_get_new_focus_child (GtkEventControllerKey *controller); - -GDK_AVAILABLE_IN_ALL -gboolean gtk_event_controller_key_contains_focus (GtkEventControllerKey *self); -GDK_AVAILABLE_IN_ALL -gboolean gtk_event_controller_key_is_focus (GtkEventControllerKey *self); - G_END_DECLS diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c index 9123bddb91..09146f4963 100644 --- a/gtk/gtkfilechooserentry.c +++ b/gtk/gtkfilechooserentry.c @@ -33,6 +33,7 @@ #include "gtkintl.h" #include "gtkmarshalers.h" #include "gtkfilefilterprivate.h" +#include "gtkeventcontrollerfocus.h" typedef struct _GtkFileChooserEntryClass GtkFileChooserEntryClass; @@ -259,12 +260,10 @@ match_func (GtkEntryCompletion *compl, } static void -chooser_entry_focus_change (GtkEventController *controller, - GtkCrossingDirection direction, - GtkFileChooserEntry *chooser_entry) +chooser_entry_focus_out (GtkEventController *controller, + GtkFileChooserEntry *chooser_entry) { - if (direction == GTK_CROSSING_OUT) - set_complete_on_load (chooser_entry, FALSE); + set_complete_on_load (chooser_entry, FALSE); } static void @@ -311,8 +310,10 @@ _gtk_file_chooser_entry_init (GtkFileChooserEntry *chooser_entry) "key-pressed", G_CALLBACK (gtk_file_chooser_entry_tab_handler), chooser_entry); + gtk_widget_add_controller (GTK_WIDGET (chooser_entry), controller); + controller = gtk_event_controller_focus_new (); g_signal_connect (controller, - "focus-change", G_CALLBACK (chooser_entry_focus_change), + "leave", G_CALLBACK (chooser_entry_focus_out), chooser_entry); gtk_widget_add_controller (GTK_WIDGET (chooser_entry), controller); diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c index d60b7b16c5..803006f95c 100644 --- a/gtk/gtkmodelbutton.c +++ b/gtk/gtkmodelbutton.c @@ -44,6 +44,7 @@ #include "gtkactionable.h" #include "gtkeventcontrollermotion.h" #include "gtkeventcontrollerkey.h" +#include "gtkeventcontrollerfocus.h" #include "gtknative.h" /** @@ -1349,21 +1350,17 @@ motion_cb (GtkEventController *controller, } static void -focus_change_cb (GtkEventController *controller, - GtkCrossingDirection direction, - gpointer data) +focus_in_cb (GtkEventController *controller, + gpointer data) { GtkWidget *target; GtkWidget *popover; - if (direction == GTK_CROSSING_IN) - { - target = gtk_event_controller_get_widget (controller); - popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU); + target = gtk_event_controller_get_widget (controller); + popover = gtk_widget_get_ancestor (target, GTK_TYPE_POPOVER_MENU); - if (popover) - gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target); - } + if (popover) + gtk_popover_menu_set_active_item (GTK_POPOVER_MENU (popover), target); } static void @@ -1390,8 +1387,8 @@ gtk_model_button_init (GtkModelButton *self) g_signal_connect (controller, "motion", G_CALLBACK (motion_cb), self); gtk_widget_add_controller (GTK_WIDGET (self), controller); - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "focus-change", G_CALLBACK (focus_change_cb), NULL); + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "enter", G_CALLBACK (focus_in_cb), NULL); gtk_widget_add_controller (GTK_WIDGET (self), controller); gesture = gtk_gesture_click_new (); diff --git a/gtk/gtkpopovermenu.c b/gtk/gtkpopovermenu.c index ef30a3d5eb..f9b494dfd3 100644 --- a/gtk/gtkpopovermenu.c +++ b/gtk/gtkpopovermenu.c @@ -28,7 +28,7 @@ #include "gtkmenutrackerprivate.h" #include "gtkpopoverprivate.h" #include "gtkwidgetprivate.h" -#include "gtkeventcontrollerkey.h" +#include "gtkeventcontrollerfocus.h" #include "gtkeventcontrollermotion.h" #include "gtkmain.h" #include "gtktypebuiltins.h" @@ -168,14 +168,12 @@ visible_submenu_changed (GObject *object, } static void -focus_change (GtkEventController *controller, - GtkCrossingDirection direction, - GtkPopoverMenu *menu) +focus_out (GtkEventController *controller, + GtkPopoverMenu *menu) { GtkWidget *new_focus = gtk_root_get_focus (gtk_widget_get_root (GTK_WIDGET (menu))); - if (direction == GTK_CROSSING_OUT && - !gtk_event_controller_key_contains_focus (GTK_EVENT_CONTROLLER_KEY (controller)) && + if (!gtk_event_controller_focus_contains_focus (GTK_EVENT_CONTROLLER_FOCUS (controller)) && new_focus != NULL) { if (menu->parent_menu && @@ -220,8 +218,8 @@ gtk_popover_menu_init (GtkPopoverMenu *popover) gtk_widget_add_css_class (GTK_WIDGET (popover), "menu"); - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "focus-change", G_CALLBACK (focus_change), popover); + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "leave", G_CALLBACK (focus_out), popover); gtk_widget_add_controller (GTK_WIDGET (popover), controller); controller = gtk_event_controller_motion_new (); diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c index 284041d168..548bac5f16 100644 --- a/gtk/gtkspinbutton.c +++ b/gtk/gtkspinbutton.c @@ -39,6 +39,7 @@ #include "gtkimage.h" #include "gtktext.h" #include "gtkeventcontrollerkey.h" +#include "gtkeventcontrollerfocus.h" #include "gtkeventcontrollermotion.h" #include "gtkeventcontrollerscroll.h" #include "gtkgestureclick.h" @@ -917,14 +918,12 @@ key_controller_key_released (GtkEventControllerKey *key, } static void -key_controller_focus_change (GtkEventController *controller, - GtkCrossingDirection direction, - GtkSpinButton *spin_button) +key_controller_focus_out (GtkEventController *controller, + GtkSpinButton *spin_button) { GtkSpinButtonPrivate *priv = gtk_spin_button_get_instance_private (spin_button); - if (direction == GTK_CROSSING_OUT && - gtk_editable_get_editable (GTK_EDITABLE (priv->entry))) + if (gtk_editable_get_editable (GTK_EDITABLE (priv->entry))) gtk_spin_button_update (spin_button); } @@ -1012,8 +1011,10 @@ gtk_spin_button_init (GtkSpinButton *spin_button) controller = gtk_event_controller_key_new (); g_signal_connect (controller, "key-released", G_CALLBACK (key_controller_key_released), spin_button); - g_signal_connect (controller, "focus-change", - G_CALLBACK (key_controller_focus_change), spin_button); + gtk_widget_add_controller (GTK_WIDGET (spin_button), controller); + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "leave", + G_CALLBACK (key_controller_focus_out), spin_button); gtk_widget_add_controller (GTK_WIDGET (spin_button), controller); } diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 9ded07605f..119e2d8386 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -34,6 +34,7 @@ #include "gtkemojichooser.h" #include "gtkemojicompletion.h" #include "gtkentrybuffer.h" +#include "gtkeventcontrollerfocus.h" #include "gtkeventcontrollerkey.h" #include "gtkeventcontrollermotion.h" #include "gtkgesturedrag.h" @@ -323,8 +324,8 @@ static void gtk_text_size_allocate (GtkWidget *widget, int baseline); static void gtk_text_snapshot (GtkWidget *widget, GtkSnapshot *snapshot); -static void gtk_text_focus_change (GtkWidget *widget, - GtkCrossingDirection direction); +static void gtk_text_focus_in (GtkWidget *widget); +static void gtk_text_focus_out (GtkWidget *widget); static gboolean gtk_text_grab_focus (GtkWidget *widget); static void gtk_text_css_changed (GtkWidget *widget, GtkCssStyleChange *change); @@ -1781,11 +1782,15 @@ gtk_text_init (GtkText *self) G_CALLBACK (gtk_text_key_controller_key_pressed), self); g_signal_connect_swapped (priv->key_controller, "im-update", G_CALLBACK (gtk_text_schedule_im_reset), self); - g_signal_connect_swapped (priv->key_controller, "focus-change", - G_CALLBACK (gtk_text_focus_change), self); gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (priv->key_controller), priv->im_context); gtk_widget_add_controller (GTK_WIDGET (self), priv->key_controller); + controller = gtk_event_controller_focus_new (); + g_signal_connect_swapped (controller, "enter", + G_CALLBACK (gtk_text_focus_in), self); + g_signal_connect_swapped (controller, "leave", + G_CALLBACK (gtk_text_focus_out), self); + gtk_widget_add_controller (GTK_WIDGET (self), controller); widget_node = gtk_widget_get_css_node (GTK_WIDGET (self)); for (i = 0; i < 2; i++) @@ -3046,53 +3051,55 @@ gtk_text_key_controller_key_pressed (GtkEventControllerKey *controller, } static void -gtk_text_focus_change (GtkWidget *widget, - GtkCrossingDirection direction) +gtk_text_focus_in (GtkWidget *widget) { GtkText *self = GTK_TEXT (widget); GtkTextPrivate *priv = gtk_text_get_instance_private (self); GdkKeymap *keymap; - if (direction == GTK_CROSSING_IN) - { - gtk_widget_queue_draw (widget); + gtk_widget_queue_draw (widget); - keymap = gdk_display_get_keymap (gtk_widget_get_display (widget)); + keymap = gdk_display_get_keymap (gtk_widget_get_display (widget)); - if (priv->editable) - { - gtk_text_schedule_im_reset (self); - gtk_im_context_focus_in (priv->im_context); - } + if (priv->editable) + { + gtk_text_schedule_im_reset (self); + gtk_im_context_focus_in (priv->im_context); + } - g_signal_connect (keymap, "direction-changed", - G_CALLBACK (keymap_direction_changed), self); + g_signal_connect (keymap, "direction-changed", + G_CALLBACK (keymap_direction_changed), self); - gtk_text_reset_blink_time (self); - gtk_text_check_cursor_blink (self); - } - else - { - gtk_text_selection_bubble_popup_unset (self); + gtk_text_reset_blink_time (self); + gtk_text_check_cursor_blink (self); +} - if (priv->text_handle) - _gtk_text_handle_set_mode (priv->text_handle, - GTK_TEXT_HANDLE_MODE_NONE); +static void +gtk_text_focus_out (GtkWidget *widget) +{ + GtkText *self = GTK_TEXT (widget); + GtkTextPrivate *priv = gtk_text_get_instance_private (self); + GdkKeymap *keymap; - gtk_widget_queue_draw (widget); + gtk_text_selection_bubble_popup_unset (self); - keymap = gdk_display_get_keymap (gtk_widget_get_display (widget)); + if (priv->text_handle) + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); - if (priv->editable) - { - gtk_text_schedule_im_reset (self); - gtk_im_context_focus_out (priv->im_context); - } + gtk_widget_queue_draw (widget); - gtk_text_check_cursor_blink (self); + keymap = gdk_display_get_keymap (gtk_widget_get_display (widget)); - g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, self); + if (priv->editable) + { + gtk_text_schedule_im_reset (self); + gtk_im_context_focus_out (priv->im_context); } + + gtk_text_check_cursor_blink (self); + + g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, self); } static gboolean diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index aeae28955f..00f4b03ee3 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -404,8 +404,8 @@ static gboolean gtk_text_view_key_controller_key_pressed (GtkEventControllerKey static void gtk_text_view_key_controller_im_update (GtkEventControllerKey *controller, GtkTextView *text_view); -static void gtk_text_view_focus_change (GtkWidget *widget, - GtkCrossingDirection direction); +static void gtk_text_view_focus_in (GtkWidget *widget); +static void gtk_text_view_focus_out (GtkWidget *widget); static void gtk_text_view_motion (GtkEventController *controller, double x, double y, @@ -1695,12 +1695,15 @@ gtk_text_view_init (GtkTextView *text_view) g_signal_connect (priv->key_controller, "im-update", G_CALLBACK (gtk_text_view_key_controller_im_update), widget); - g_signal_connect_swapped (priv->key_controller, "focus-change", - G_CALLBACK (gtk_text_view_focus_change), - widget); gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (priv->key_controller), priv->im_context); gtk_widget_add_controller (widget, priv->key_controller); + controller = gtk_event_controller_focus_new (); + g_signal_connect_swapped (controller, "enter", + G_CALLBACK (gtk_text_view_focus_in), widget); + g_signal_connect_swapped (controller, "leave", + G_CALLBACK (gtk_text_view_focus_out), widget); + gtk_widget_add_controller (widget, controller); priv->selection_node = gtk_css_node_new (); gtk_css_node_set_name (priv->selection_node, g_quark_from_static_string ("selection")); @@ -5307,65 +5310,66 @@ keymap_direction_changed (GdkKeymap *keymap, } static void -gtk_text_view_focus_change (GtkWidget *widget, - GtkCrossingDirection direction) +gtk_text_view_focus_in (GtkWidget *widget) { GtkTextView *text_view = GTK_TEXT_VIEW (widget); GtkTextViewPrivate *priv = text_view->priv; - if (direction == GTK_CROSSING_IN) - { - gtk_widget_queue_draw (widget); + gtk_widget_queue_draw (widget); - DV(g_print (G_STRLOC": focus_in\n")); + DV(g_print (G_STRLOC": focus_in\n")); - gtk_text_view_reset_blink_time (text_view); + gtk_text_view_reset_blink_time (text_view); - if (cursor_visible (text_view) && priv->layout) - { - gtk_text_layout_set_cursor_visible (priv->layout, TRUE); - gtk_text_view_check_cursor_blink (text_view); - } + if (cursor_visible (text_view) && priv->layout) + { + gtk_text_layout_set_cursor_visible (priv->layout, TRUE); + gtk_text_view_check_cursor_blink (text_view); + } - g_signal_connect (gdk_display_get_keymap (gtk_widget_get_display (widget)), - "direction-changed", - G_CALLBACK (keymap_direction_changed), text_view); - gtk_text_view_check_keymap_direction (text_view); + g_signal_connect (gdk_display_get_keymap (gtk_widget_get_display (widget)), + "direction-changed", + G_CALLBACK (keymap_direction_changed), text_view); + gtk_text_view_check_keymap_direction (text_view); - if (priv->editable) - { - priv->need_im_reset = TRUE; - gtk_im_context_focus_in (priv->im_context); - } + if (priv->editable) + { + priv->need_im_reset = TRUE; + gtk_im_context_focus_in (priv->im_context); } - else - { - gtk_text_view_end_selection_drag (text_view); +} - gtk_widget_queue_draw (widget); +static void +gtk_text_view_focus_out (GtkWidget *widget) +{ + GtkTextView *text_view = GTK_TEXT_VIEW (widget); + GtkTextViewPrivate *priv = text_view->priv; - DV(g_print (G_STRLOC": focus_out\n")); + gtk_text_view_end_selection_drag (text_view); - if (cursor_visible (text_view) && priv->layout) - { - gtk_text_view_check_cursor_blink (text_view); - gtk_text_layout_set_cursor_visible (priv->layout, FALSE); - } + gtk_widget_queue_draw (widget); - g_signal_handlers_disconnect_by_func (gdk_display_get_keymap (gtk_widget_get_display (widget)), - keymap_direction_changed, - text_view); - gtk_text_view_selection_bubble_popup_unset (text_view); + DV(g_print (G_STRLOC": focus_out\n")); - if (priv->text_handle) - _gtk_text_handle_set_mode (priv->text_handle, - GTK_TEXT_HANDLE_MODE_NONE); + if (cursor_visible (text_view) && priv->layout) + { + gtk_text_view_check_cursor_blink (text_view); + gtk_text_layout_set_cursor_visible (priv->layout, FALSE); + } - if (priv->editable) - { - priv->need_im_reset = TRUE; - gtk_im_context_focus_out (priv->im_context); - } + g_signal_handlers_disconnect_by_func (gdk_display_get_keymap (gtk_widget_get_display (widget)), + keymap_direction_changed, + text_view); + gtk_text_view_selection_bubble_popup_unset (text_view); + + if (priv->text_handle) + _gtk_text_handle_set_mode (priv->text_handle, + GTK_TEXT_HANDLE_MODE_NONE); + + if (priv->editable) + { + priv->need_im_reset = TRUE; + gtk_im_context_focus_out (priv->im_context); } } diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index 227bd3c58d..b203ed5770 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -37,6 +37,7 @@ #include "gtkentryprivate.h" #include "gtksearchentryprivate.h" #include "gtkeventcontrollerkey.h" +#include "gtkeventcontrollerfocus.h" #include "gtkeventcontrollermotion.h" #include "gtkeventcontrollerscroll.h" #include "gtkframe.h" @@ -667,8 +668,7 @@ static void gtk_tree_view_key_controller_key_released (GtkEventControllerKey guint keycode, GdkModifierType state, GtkTreeView *tree_view); -static void gtk_tree_view_key_controller_focus_change (GtkEventController *key, - GtkCrossingDirection direction, +static void gtk_tree_view_focus_controller_focus_out (GtkEventController *focus, GtkTreeView *tree_view); static gint gtk_tree_view_focus (GtkWidget *widget, @@ -1832,8 +1832,11 @@ gtk_tree_view_init (GtkTreeView *tree_view) G_CALLBACK (gtk_tree_view_key_controller_key_pressed), tree_view); g_signal_connect (controller, "key-released", G_CALLBACK (gtk_tree_view_key_controller_key_released), tree_view); - g_signal_connect (controller, "focus-change", - G_CALLBACK (gtk_tree_view_key_controller_focus_change), tree_view); + gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); + + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "leave", + G_CALLBACK (gtk_tree_view_focus_controller_focus_out), tree_view); gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); } @@ -5537,19 +5540,15 @@ gtk_tree_view_motion_controller_pointer (GtkEventControllerMotion *controller, } static void -gtk_tree_view_key_controller_focus_change (GtkEventController *key, - GtkCrossingDirection direction, - GtkTreeView *tree_view) +gtk_tree_view_focus_controller_focus_out (GtkEventController *focus, + GtkTreeView *tree_view) { - if (direction == GTK_CROSSING_OUT) - { - gtk_widget_queue_draw (GTK_WIDGET (tree_view)); + gtk_widget_queue_draw (GTK_WIDGET (tree_view)); - if (tree_view->search_popover && - !gtk_event_controller_key_contains_focus (GTK_EVENT_CONTROLLER_KEY (key))) - gtk_tree_view_search_popover_hide (tree_view->search_popover, tree_view, - gtk_get_current_event_device ()); - } + if (tree_view->search_popover && + !gtk_event_controller_focus_contains_focus (GTK_EVENT_CONTROLLER_FOCUS (focus))) + gtk_tree_view_search_popover_hide (tree_view->search_popover, tree_view, + gtk_get_current_event_device ()); } /* Incremental Reflow diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c index 790e611036..a70c893a49 100644 --- a/gtk/gtktreeviewcolumn.c +++ b/gtk/gtktreeviewcolumn.c @@ -35,6 +35,7 @@ #include "gtktypebuiltins.h" #include "gtkwidgetprivate.h" #include "gtkgesturedrag.h" +#include "gtkeventcontrollerfocus.h" #include "gtkeventcontrollerkey.h" #include "a11y/gtktreeviewaccessibleprivate.h" @@ -831,11 +832,9 @@ gtk_tree_view_column_cell_layout_get_area (GtkCellLayout *cell_layout) static void focus_in (GtkEventControllerKey *controller, - GtkCrossingDirection direction, GtkTreeViewColumn *column) { - if (direction == GTK_CROSSING_IN) - _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column); + _gtk_tree_view_set_focus_column (GTK_TREE_VIEW (column->priv->tree_view), column); } /* Button handling code @@ -865,8 +864,8 @@ gtk_tree_view_column_create_button (GtkTreeViewColumn *tree_column) gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); gtk_widget_add_controller (priv->button, controller); - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "focus-change", G_CALLBACK (focus_in), tree_column); + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "enter", G_CALLBACK (focus_in), tree_column); gtk_widget_add_controller (priv->button, controller); priv->frame = gtk_frame_new (NULL); diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index d399d3c7d7..9a60d813d1 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -41,6 +41,7 @@ #include "gtkcssshadowvalueprivate.h" #include "gtkcssstylepropertyprivate.h" #include "gtkdragdest.h" +#include "gtkeventcontrollerfocus.h" #include "gtkeventcontrollerkey.h" #include "gtkeventcontrollermotion.h" #include "gtkgesturedrag.h" @@ -394,8 +395,8 @@ static void gtk_window_size_allocate (GtkWidget *widget, int height, int baseline); static gboolean gtk_window_close_request (GtkWindow *window); -static void gtk_window_focus_change (GtkWidget *widget, - GtkCrossingDirection direction); +static void gtk_window_focus_in (GtkWidget *widget); +static void gtk_window_focus_out (GtkWidget *widget); static gboolean gtk_window_key_pressed (GtkWidget *widget, guint keyval, guint keycode, @@ -1772,6 +1773,7 @@ gtk_window_init (GtkWindow *window) GtkCssNode *widget_node; GdkSeat *seat; GtkEventController *motion_controller; + GtkEventController *controller; #ifdef GDK_WINDOWING_X11 GtkDropTarget *dest; #endif @@ -1843,13 +1845,17 @@ gtk_window_init (GtkWindow *window) priv->key_controller = gtk_event_controller_key_new (); gtk_event_controller_set_propagation_phase (priv->key_controller, GTK_PHASE_CAPTURE); - g_signal_connect_swapped (priv->key_controller, "focus-change", - G_CALLBACK (gtk_window_focus_change), window); g_signal_connect_swapped (priv->key_controller, "key-pressed", G_CALLBACK (gtk_window_key_pressed), window); g_signal_connect_swapped (priv->key_controller, "key-released", G_CALLBACK (gtk_window_key_released), window); gtk_widget_add_controller (widget, priv->key_controller); + controller = gtk_event_controller_focus_new (); + g_signal_connect_swapped (controller, "enter", + G_CALLBACK (gtk_window_focus_in), window); + g_signal_connect_swapped (controller, "leave", + G_CALLBACK (gtk_window_focus_out), window); + gtk_widget_add_controller (widget, controller); /* Shared constraint solver */ priv->constraint_solver = gtk_constraint_solver_new (); @@ -6120,33 +6126,33 @@ gtk_window_has_mnemonic_modifier_pressed (GtkWindow *window) } static void -gtk_window_focus_change (GtkWidget *widget, - GtkCrossingDirection direction) +gtk_window_focus_in (GtkWidget *widget) { GtkWindow *window = GTK_WINDOW (widget); - if (direction == GTK_CROSSING_IN) + /* It appears spurious focus in events can occur when + * the window is hidden. So we'll just check to see if + * the window is visible before actually handling the + * event + */ + if (gtk_widget_get_visible (widget)) { - /* It appears spurious focus in events can occur when - * the window is hidden. So we'll just check to see if - * the window is visible before actually handling the - * event - */ - if (gtk_widget_get_visible (widget)) - { - _gtk_window_set_is_active (window, TRUE); + _gtk_window_set_is_active (window, TRUE); - if (gtk_window_has_mnemonic_modifier_pressed (window)) - _gtk_window_schedule_mnemonics_visible (window); - } + if (gtk_window_has_mnemonic_modifier_pressed (window)) + _gtk_window_schedule_mnemonics_visible (window); } - else - { - _gtk_window_set_is_active (window, FALSE); +} - /* set the mnemonic-visible property to false */ - gtk_window_set_mnemonics_visible (window, FALSE); - } +static void +gtk_window_focus_out (GtkWidget *widget) +{ + GtkWindow *window = GTK_WINDOW (widget); + + _gtk_window_set_is_active (window, FALSE); + + /* set the mnemonic-visible property to false */ + gtk_window_set_mnemonics_visible (window, FALSE); } static void diff --git a/gtk/meson.build b/gtk/meson.build index b57a7e33ad..a09b672130 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -218,6 +218,7 @@ gtk_public_sources = files([ 'gtkentrybuffer.c', 'gtkentrycompletion.c', 'gtkeventcontroller.c', + 'gtkeventcontrollerfocus.c', 'gtkeventcontrollerkey.c', 'gtkeventcontrollerlegacy.c', 'gtkeventcontrollermotion.c', diff --git a/testsuite/gtk/focus.c b/testsuite/gtk/focus.c index ac6d43fd7b..854236d57d 100644 --- a/testsuite/gtk/focus.c +++ b/testsuite/gtk/focus.c @@ -14,17 +14,27 @@ widget_name (GtkWidget *widget) } static void -focus_change (GtkEventControllerKey *key, - GtkCrossingDirection direction, - GString *s) +focus_in (GtkEventControllerFocus *key, + GString *s) { GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (key)); - g_string_append_printf (s, "%s: focus-change %s is-focus: %d contains-focus: %d\n", + g_string_append_printf (s, "%s: focus-in is-focus: %d contains-focus: %d\n", widget_name (widget), - direction == GTK_CROSSING_IN ? "in" : "out", - gtk_event_controller_key_is_focus (key), - gtk_event_controller_key_contains_focus (key)); + gtk_event_controller_focus_is_focus (key), + gtk_event_controller_focus_contains_focus (key)); +} + +static void +focus_out (GtkEventControllerFocus *key, + GString *s) +{ + GtkWidget *widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (key)); + + g_string_append_printf (s, "%s: focus-out is-focus: %d contains-focus: %d\n", + widget_name (widget), + gtk_event_controller_focus_is_focus (key), + gtk_event_controller_focus_contains_focus (key)); } static void @@ -32,8 +42,9 @@ add_controller (GtkWidget *widget, GString *s) { GtkEventController *controller; - controller = gtk_event_controller_key_new (); - g_signal_connect (controller, "focus-change", G_CALLBACK (focus_change), s); + controller = gtk_event_controller_focus_new (); + g_signal_connect (controller, "enter", G_CALLBACK (focus_in), s); + g_signal_connect (controller, "leave", G_CALLBACK (focus_out), s); gtk_widget_add_controller (widget, controller); } @@ -107,8 +118,8 @@ test_window_focus (void) g_print ("-> box\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"window: focus-change in is-focus: 0 contains-focus: 1\n" -"box: focus-change in is-focus: 1 contains-focus: 1\n" +"window: focus-in is-focus: 0 contains-focus: 1\n" +"box: focus-in is-focus: 1 contains-focus: 1\n" ); g_string_truncate (s, 0); @@ -118,12 +129,12 @@ test_window_focus (void) g_print ("box -> entry1\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"box: focus-change out is-focus: 0 contains-focus: 0\n" -"window: focus-change out is-focus: 0 contains-focus: 0\n" -"window: focus-change in is-focus: 0 contains-focus: 1\n" -"box: focus-change in is-focus: 0 contains-focus: 1\n" -"box1: focus-change in is-focus: 0 contains-focus: 1\n" -"entry1: focus-change in is-focus: 1 contains-focus: 1\n" +"box: focus-out is-focus: 0 contains-focus: 0\n" +"window: focus-out is-focus: 0 contains-focus: 0\n" +"window: focus-in is-focus: 0 contains-focus: 1\n" +"box: focus-in is-focus: 0 contains-focus: 1\n" +"box1: focus-in is-focus: 0 contains-focus: 1\n" +"entry1: focus-in is-focus: 1 contains-focus: 1\n" ); g_string_truncate (s, 0); @@ -136,14 +147,14 @@ test_window_focus (void) g_print ("entry1 -> entry2\n%s\n", s->str); g_assert_cmpstr (s->str, ==, -"entry1: focus-change out is-focus: 0 contains-focus: 0\n" -"box1: focus-change out is-focus: 0 contains-focus: 0\n" -"box: focus-change out is-focus: 0 contains-focus: 0\n" -"window: focus-change out is-focus: 0 contains-focus: 0\n" -"window: focus-change in is-focus: 0 contains-focus: 1\n" -"box: focus-change in is-focus: 0 contains-focus: 1\n" -"box2: focus-change in is-focus: 0 contains-focus: 1\n" -"entry2: focus-change in is-focus: 1 contains-focus: 1\n" +"entry1: focus-out is-focus: 0 contains-focus: 0\n" +"box1: focus-out is-focus: 0 contains-focus: 0\n" +"box: focus-out is-focus: 0 contains-focus: 0\n" +"window: focus-out is-focus: 0 contains-focus: 0\n" +"window: focus-in is-focus: 0 contains-focus: 1\n" +"box: focus-in is-focus: 0 contains-focus: 1\n" +"box2: focus-in is-focus: 0 contains-focus: 1\n" +"entry2: focus-in is-focus: 1 contains-focus: 1\n" ); g_string_truncate (s, 0); @@ -156,12 +167,12 @@ test_window_focus (void) g_print ("entry2 -> box\n%s", s->str); g_assert_cmpstr (s->str, ==, -"entry2: focus-change out is-focus: 0 contains-focus: 0\n" -"box2: focus-change out is-focus: 0 contains-focus: 0\n" -"box: focus-change out is-focus: 0 contains-focus: 0\n" -"window: focus-change out is-focus: 0 contains-focus: 0\n" -"window: focus-change in is-focus: 0 contains-focus: 1\n" -"box: focus-change in is-focus: 1 contains-focus: 1\n" +"entry2: focus-out is-focus: 0 contains-focus: 0\n" +"box2: focus-out is-focus: 0 contains-focus: 0\n" +"box: focus-out is-focus: 0 contains-focus: 0\n" +"window: focus-out is-focus: 0 contains-focus: 0\n" +"window: focus-in is-focus: 0 contains-focus: 1\n" +"box: focus-in is-focus: 1 contains-focus: 1\n" ); g_string_truncate (s, 0); -- 2.30.2